//触发更新视图
function updateView() {
  console.log("视图更新");
}
//重新定义属性，监听起来
function defineReactive(target, key, value) {
  //因数据可能是对象里面多次嵌套，需递归，深度监听
  observer(value);
  Object.defineProperty(target, key, {
    get() {
      return value;
    },
    set(newValue) {
      //设置的新值,可能是对象，需递归，深度监听
      observer(newValue);
      //设置新值
      //value一直在闭包中，此时设置完之后，再get时也是会获取最新的值
      if (newValue !== value) {
        value = newValue;
        updateView();
      }
    },
  });
}

//重新定义数组原型
const oldArrayProperty = Array.prototype//数组的所有方法都在里面
console.log(oldArrayProperty.push,8888);
//创建新对象,原型指向 oldArrayProperty,再扩展新的方法不会影响Array原型（同名也不会）
const arrProto = Object.create(oldArrayProperty);
//自建对象一定要指向数组的原型麻？实例上面没有自定义的方法，就会在原型上找
['push','pop','shift','unshift','splice'].forEach(methodName=>{
  arrProto[methodName] = function(){
    updateView()
    console.log(this,888);
    //找到数组的原型方法去调用
    //不知道属性call可去看
    // https://www.w3school.com.cn/js/js_function_call.asp
    //...arguments 未知有多少参数的写法
    oldArrayProperty[methodName].call(this,...arguments)
    //Array.prototype.push.call(this,...arguments)
  }
})


//监听对象属性
function observer(target) {
  if (typeof target !== "object" || target === null) {
    //不是对象或数组
    return target;
  }

  //污染全局的Array原型
  // Array.prototype.push = function(){
  //   updateView()
  //   ...arguments
  // }
 if(Array.isArray(target)){
  //把数组的原型重新赋值为自己的
  target.__proto__ = arrProto
 }

  
  //重新定义各个属性(for in 也可以遍历数组)
  //数组里的值会绑定getter/setter
  for (let key in target) {
    console.log(target,key,64);
    defineReactive(target, key, target[key]);
  }
}




//准备数据
const data = {
  // name: "陈思元",
  // age: 6,
  // info: {
  //   address: "北京",
  // },
  nums:[10,20,30]
};

observer(data);
console.log(data.nums[0]);

// data.name = '袁六六'
// data.age = 9
//浅监听,修改info可以监听到，修改里面的属性address监听不到
// data.info.address = "上海";
// data.x = '新增属性X'//新增属性监听不到 -- 所以需要vue.set
// delete data.name //删除属性，监听不到 -- 所以需要vue.delete
data.nums.push('999')
/**
 * Object.defineProperty的缺点
 *深度监听，需要递归到底，一次性计算量大(不一次性计算)
 *无法监听新增属性/删除属性(vue.set vue.delete)
 *无法原生监听数组，需要特殊处理
 */
// console.log(data.info.address);
